{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "f4169bfb-769a-4db3-833e-c827f19024b2",
      "metadata": {
        "id": "f4169bfb-769a-4db3-833e-c827f19024b2"
      },
      "source": [
        "# Parallel node execution\n",
        "\n",
        "## Review\n",
        "\n",
        "In module 3, we went in-depth on `human-in-the loop`, showing 3 common use-cases:\n",
        "\n",
        "(1) `Approval` - We can interrupt our agent, surface state to a user, and allow the user to accept an action\n",
        "\n",
        "(2) `Debugging` - We can rewind the graph to reproduce or avoid issues\n",
        "\n",
        "(3) `Editing` - You can modify the state\n",
        "\n",
        "## Goals\n",
        "\n",
        "This module will build on `human-in-the-loop` as well as the `memory` concepts discussed in module 2.\n",
        "\n",
        "We will dive into `multi-agent` workflows, and build up to a multi-agent research assistant that ties together all of the modules from this course.\n",
        "\n",
        "To build this multi-agent research assistant, we'll first discuss a few LangGraph controllability topics.\n",
        "\n",
        "We'll start with [parallelization](https://langchain-ai.github.io/langgraph/how-tos/branching/#how-to-create-branches-for-parallel-node-execution).\n",
        "\n",
        "## Fan out and fan in\n",
        "\n",
        "Let's build a simple linear graph that over-writes the state at each step."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 1,
      "id": "618eab5c-4ef7-4273-8e0b-a9c847897ed7",
      "metadata": {
        "id": "618eab5c-4ef7-4273-8e0b-a9c847897ed7"
      },
      "outputs": [],
      "source": [
        "%%capture --no-stderr\n",
        "%pip install -U  langgraph tavily-python wikipedia langchain_google_genai langchain_community langgraph_sdk"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "31bbec0d",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "31bbec0d",
        "outputId": "5a6a8a75-a5cd-4caf-de41-74bd1e5d96c9"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "env: GOOGLE_API_KEY=AIzaSyDojJx56O8nzFauBKc_rj_0o7wvAnQgHAI\n",
            "env: LANGCHAIN_API_KEY=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\n"
          ]
        }
      ],
      "source": [
        "import os\n",
        "from google.colab import userdata\n",
        "\n",
        "%env GOOGLE_API_KEY = {userdata.get('GEMINI_API_KEY')}\n",
        "\n",
        "%env LANGCHAIN_API_KEY = {userdata.get('LANGCHAIN_API_KEY')}\n",
        "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
        "os.environ[\"LANGCHAIN_PROJECT\"] = \"langchain-academy\""
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "id": "1dd77093-1794-4bd7-8c57-58f59a74c20b",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 548
        },
        "id": "1dd77093-1794-4bd7-8c57-58f59a74c20b",
        "outputId": "ae54637d-3cf4-48ed-801c-8f3a9a5f962d"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/jpeg": "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\n",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {}
        }
      ],
      "source": [
        "from IPython.display import Image, display\n",
        "\n",
        "from typing import Any\n",
        "from typing_extensions import TypedDict\n",
        "\n",
        "from langgraph.graph import StateGraph, START, END\n",
        "from langgraph.graph.state import CompiledStateGraph\n",
        "\n",
        "class State(TypedDict):\n",
        "    # The operator.add reducer fn makes this append-only\n",
        "    state: str\n",
        "\n",
        "class ReturnNodeValue:\n",
        "    def __init__(self, node_secret: str):\n",
        "        self._value = node_secret\n",
        "\n",
        "    def __call__(self, state: State) -> Any:\n",
        "        print(f\"Adding {self._value} to {state['state']}\")\n",
        "        return {\"state\": [self._value]}\n",
        "\n",
        "# Add nodes\n",
        "builder: StateGraph = StateGraph(State)\n",
        "\n",
        "# Initialize each node with node_secret\n",
        "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
        "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
        "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
        "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
        "\n",
        "# Flow\n",
        "builder.add_edge(START, \"a\")\n",
        "builder.add_edge(\"a\", \"b\")\n",
        "builder.add_edge(\"b\", \"c\")\n",
        "builder.add_edge(\"c\", \"d\")\n",
        "builder.add_edge(\"d\", END)\n",
        "graph: CompiledStateGraph = builder.compile()\n",
        "\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bdd027d3-ef9f-4d43-b190-e9f07d521e18",
      "metadata": {
        "id": "bdd027d3-ef9f-4d43-b190-e9f07d521e18"
      },
      "source": [
        "We over-write state, as expected."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "id": "bf260088-90d5-45b2-93ab-42f241560840",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "bf260088-90d5-45b2-93ab-42f241560840",
        "outputId": "b0d02d71-a078-4659-e360-d493930979f8"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Adding I'm A to []\n",
            "Adding I'm B to [\"I'm A\"]\n",
            "Adding I'm C to [\"I'm B\"]\n",
            "Adding I'm D to [\"I'm C\"]\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'state': [\"I'm D\"]}"
            ]
          },
          "metadata": {},
          "execution_count": 26
        }
      ],
      "source": [
        "graph.invoke({\"state\": []})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a9dec27d-dc43-4088-beb2-53ad090d2971",
      "metadata": {
        "id": "a9dec27d-dc43-4088-beb2-53ad090d2971"
      },
      "source": [
        "Now, let's run `b` and `c` in parallel.\n",
        "\n",
        "And then run `d`.\n",
        "\n",
        "We can do this easily with fan-out from `a` to `b` and `c`, and then fan-in to `d`.\n",
        "\n",
        "The the state updates are applied at the end of each step.\n",
        "\n",
        "Let's run it."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 27,
      "id": "8fdeaaab-a8c3-470f-89ef-9cf0a2760667",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 449
        },
        "id": "8fdeaaab-a8c3-470f-89ef-9cf0a2760667",
        "outputId": "4c3c6e4a-c8b6-435e-87b0-734c02ca2c11"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/jpeg": "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\n",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {}
        }
      ],
      "source": [
        "builder: StateGraph = StateGraph(State)\n",
        "\n",
        "# Initialize each node with node_secret\n",
        "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
        "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
        "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
        "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
        "\n",
        "# Flow\n",
        "builder.add_edge(START, \"a\")\n",
        "builder.add_edge(\"a\", \"b\")\n",
        "builder.add_edge(\"a\", \"c\")\n",
        "builder.add_edge(\"b\", \"d\")\n",
        "builder.add_edge(\"c\", \"d\")\n",
        "builder.add_edge(\"d\", END)\n",
        "graph: CompiledStateGraph = builder.compile()\n",
        "\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "35238fde-0230-4ae8-9200-158a8835c4f1",
      "metadata": {
        "id": "35238fde-0230-4ae8-9200-158a8835c4f1"
      },
      "source": [
        "**We see an error**!\n",
        "\n",
        "This is because both `b` and `c` are writing to the same state key / channel in the same step."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 28,
      "id": "9048b041-6849-4f09-9811-6b7a80f67859",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "9048b041-6849-4f09-9811-6b7a80f67859",
        "outputId": "5d32b2cb-3b84-4070-b67d-fa2f8695998d"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Adding I'm A to []\n",
            "Adding I'm C to [\"I'm A\"]Adding I'm B to [\"I'm A\"]\n",
            "\n",
            "An error occurred: At key 'state': Can receive only one value per step. Use an Annotated key to handle multiple values.\n"
          ]
        }
      ],
      "source": [
        "from langgraph.errors import InvalidUpdateError\n",
        "try:\n",
        "    graph.invoke({\"state\": []})\n",
        "except InvalidUpdateError as e:\n",
        "    print(f\"An error occurred: {e}\")"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "64cc329d-59fa-4c26-adcf-9122a824955d",
      "metadata": {
        "id": "64cc329d-59fa-4c26-adcf-9122a824955d"
      },
      "source": [
        "When using fan out, we need to be sure that we are using a reducer if steps are writing to the same the channel / key.\n",
        "\n",
        "As we touched on in Module 2, `operator.add` is a function from Python's built-in operator module.\n",
        "\n",
        "When `operator.add` is applied to lists, it performs list concatenation."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 29,
      "id": "8f1292ac-510a-4801-b2a3-e2c6d2d9582a",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 449
        },
        "id": "8f1292ac-510a-4801-b2a3-e2c6d2d9582a",
        "outputId": "e2056b50-6932-43ea-c389-60262dae1cd1"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/jpeg": "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\n",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {}
        }
      ],
      "source": [
        "import operator\n",
        "from typing import Annotated\n",
        "\n",
        "class State(TypedDict):\n",
        "    # The operator.add reducer fn makes this append-only\n",
        "    state: Annotated[list, operator.add]\n",
        "\n",
        "# Add nodes\n",
        "builder: StateGraph = StateGraph(State)\n",
        "\n",
        "# Initialize each node with node_secret\n",
        "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
        "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
        "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
        "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
        "\n",
        "# Flow\n",
        "builder.add_edge(START, \"a\")\n",
        "builder.add_edge(\"a\", \"b\")\n",
        "builder.add_edge(\"a\", \"c\")\n",
        "builder.add_edge(\"b\", \"d\")\n",
        "builder.add_edge(\"c\", \"d\")\n",
        "builder.add_edge(\"d\", END)\n",
        "graph: CompiledStateGraph = builder.compile()\n",
        "\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 30,
      "id": "ffbad231-fc1d-49b1-a9fc-ed9153fa3977",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ffbad231-fc1d-49b1-a9fc-ed9153fa3977",
        "outputId": "84334b9f-21f5-413d-b6de-0f1c8956dc8c"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Adding I'm A to []\n",
            "Adding I'm B to [\"I'm A\"]\n",
            "Adding I'm C to [\"I'm A\"]\n",
            "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm C\"]\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'state': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm D\"]}"
            ]
          },
          "metadata": {},
          "execution_count": 30
        }
      ],
      "source": [
        "graph.invoke({\"state\": []})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bdf5baa2-cecd-44b6-b0c4-d258340783f8",
      "metadata": {
        "id": "bdf5baa2-cecd-44b6-b0c4-d258340783f8"
      },
      "source": [
        "Now we see that we append to state for the updates made in parallel by `b` and `c`."
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ed6fc7c7-198d-41be-867f-e77c93ba3217",
      "metadata": {
        "id": "ed6fc7c7-198d-41be-867f-e77c93ba3217"
      },
      "source": [
        "## Waiting for nodes to finish\n",
        "\n",
        "Now, lets consider a case where one parallel path has more steps than the other one."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 31,
      "id": "f50b5d4f-dd39-4c22-b623-e0abc23f9144",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 548
        },
        "id": "f50b5d4f-dd39-4c22-b623-e0abc23f9144",
        "outputId": "f8c4ad39-3585-4e00-c24b-22eeea3acfc5"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/jpeg": "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\n",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {}
        }
      ],
      "source": [
        "builder: StateGraph = StateGraph(State)\n",
        "\n",
        "# Initialize each node with node_secret\n",
        "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
        "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
        "builder.add_node(\"b2\", ReturnNodeValue(\"I'm B2\"))\n",
        "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
        "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
        "\n",
        "# Flow\n",
        "builder.add_edge(START, \"a\")\n",
        "builder.add_edge(\"a\", \"b\")\n",
        "builder.add_edge(\"a\", \"c\")\n",
        "builder.add_edge(\"b\", \"b2\")\n",
        "builder.add_edge([\"b2\", \"c\"], \"d\")\n",
        "builder.add_edge(\"d\", END)\n",
        "graph: CompiledStateGraph = builder.compile()\n",
        "\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "11640e6f-ac62-4ad4-89d9-7f6f9b56bf7a",
      "metadata": {
        "id": "11640e6f-ac62-4ad4-89d9-7f6f9b56bf7a"
      },
      "source": [
        "In this case, `b`, `b2`, and `c` are all part of the same step.\n",
        "\n",
        "The graph will wait for all of these to be completed before proceeding to step `d`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 32,
      "id": "fafda930-e75b-410f-ba93-eb5fc0219303",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "fafda930-e75b-410f-ba93-eb5fc0219303",
        "outputId": "9f806b07-1bac-4fdc-bd02-5ad8b707ed04"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Adding I'm A to []\n",
            "Adding I'm B to [\"I'm A\"]\n",
            "Adding I'm C to [\"I'm A\"]\n",
            "Adding I'm B2 to [\"I'm A\", \"I'm B\", \"I'm C\"]\n",
            "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm B2\"]\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'state': [\"I'm A\", \"I'm B\", \"I'm C\", \"I'm B2\", \"I'm D\"]}"
            ]
          },
          "metadata": {},
          "execution_count": 32
        }
      ],
      "source": [
        "graph.invoke({\"state\": []})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "6610a2e3-b053-47e8-bf4e-0968dfaa0a5d",
      "metadata": {
        "id": "6610a2e3-b053-47e8-bf4e-0968dfaa0a5d"
      },
      "source": [
        "## Setting the order of state updates\n",
        "\n",
        "However, within each step we don't have specific control over the order of the state updates!\n",
        "\n",
        "In simple terms, it is a deterministic order determined by LangGraph based upon graph topology that **we do not control**.\n",
        "\n",
        "Above, we see that `c` is added before `b2`.\n",
        "\n",
        "However, we can use a custom reducer to customize this e.g., sort state updates."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 33,
      "id": "24788e73-0950-432e-ad32-7987ea076529",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 548
        },
        "id": "24788e73-0950-432e-ad32-7987ea076529",
        "outputId": "5c72ffdb-4e48-4f36-d9b6-64b093306a88"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/jpeg": "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\n",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {}
        }
      ],
      "source": [
        "def sorting_reducer(left, right):\n",
        "    \"\"\" Combines and sorts the values in a list\"\"\"\n",
        "    if not isinstance(left, list):\n",
        "        left = [left]\n",
        "\n",
        "    if not isinstance(right, list):\n",
        "        right = [right]\n",
        "\n",
        "    return sorted(left + right, reverse=False)\n",
        "\n",
        "class State(TypedDict):\n",
        "    # sorting_reducer will sort the values in state\n",
        "    state: Annotated[list, sorting_reducer]\n",
        "\n",
        "# Add nodes\n",
        "builder: StateGraph = StateGraph(State)\n",
        "\n",
        "# Initialize each node with node_secret\n",
        "builder.add_node(\"a\", ReturnNodeValue(\"I'm A\"))\n",
        "builder.add_node(\"b\", ReturnNodeValue(\"I'm B\"))\n",
        "builder.add_node(\"b2\", ReturnNodeValue(\"I'm B2\"))\n",
        "builder.add_node(\"c\", ReturnNodeValue(\"I'm C\"))\n",
        "builder.add_node(\"d\", ReturnNodeValue(\"I'm D\"))\n",
        "\n",
        "# Flow\n",
        "builder.add_edge(START, \"a\")\n",
        "builder.add_edge(\"a\", \"b\")\n",
        "builder.add_edge(\"a\", \"c\")\n",
        "builder.add_edge(\"b\", \"b2\")\n",
        "builder.add_edge([\"b2\", \"c\"], \"d\")\n",
        "builder.add_edge(\"d\", END)\n",
        "graph: CompiledStateGraph = builder.compile()\n",
        "\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 34,
      "id": "607dba2e-f9f0-4bc7-8ba6-684521a49bdc",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "607dba2e-f9f0-4bc7-8ba6-684521a49bdc",
        "outputId": "fd5c0634-fbe4-46a3-d01f-f133b8991f42"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Adding I'm A to []\n",
            "Adding I'm C to [\"I'm A\"]Adding I'm B to [\"I'm A\"]\n",
            "\n",
            "Adding I'm B2 to [\"I'm A\", \"I'm B\", \"I'm C\"]\n",
            "Adding I'm D to [\"I'm A\", \"I'm B\", \"I'm B2\", \"I'm C\"]\n"
          ]
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'state': [\"I'm A\", \"I'm B\", \"I'm B2\", \"I'm C\", \"I'm D\"]}"
            ]
          },
          "metadata": {},
          "execution_count": 34
        }
      ],
      "source": [
        "graph.invoke({\"state\": []})"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "fb1714c0-e881-48e7-bcb8-a60016f0485e",
      "metadata": {
        "id": "fb1714c0-e881-48e7-bcb8-a60016f0485e"
      },
      "source": [
        "Now, the reducer sorts the updated state values!"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "34e0750b-e6af-40d9-835c-c664da5a2d3b",
      "metadata": {
        "id": "34e0750b-e6af-40d9-835c-c664da5a2d3b"
      },
      "source": [
        "## Working with LLMs\n",
        "\n",
        "Now, lets add a realistic example!\n",
        "\n",
        "We want to gather context from two external sources (Wikipedia and Web-Seach) and have an LLM answer a question."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 35,
      "id": "e1e9d03c-cb41-415c-862d-c9616d5a2d07",
      "metadata": {
        "id": "e1e9d03c-cb41-415c-862d-c9616d5a2d07"
      },
      "outputs": [],
      "source": [
        "from langchain_google_genai import ChatGoogleGenerativeAI\n",
        "llm: ChatGoogleGenerativeAI = ChatGoogleGenerativeAI(model = \"gemini-1.5-flash\")\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 36,
      "id": "0f75cc78-d1a1-47a5-8648-bf5a79c883de",
      "metadata": {
        "id": "0f75cc78-d1a1-47a5-8648-bf5a79c883de"
      },
      "outputs": [],
      "source": [
        "class State(TypedDict):\n",
        "    question: str\n",
        "    answer: str\n",
        "    context: Annotated[list, operator.add]"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "9e714ea8-095c-461a-98bc-ee782a84ef5c",
      "metadata": {
        "id": "9e714ea8-095c-461a-98bc-ee782a84ef5c"
      },
      "source": [
        "You can try different web search tools. [Tavily](https://tavily.com/) is one nice option to consider, but ensure your `TAVILY_API_KEY` is set."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 37,
      "id": "c8bb519a-d08a-4ec7-8f0b-2ce6a9bf7342",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "c8bb519a-d08a-4ec7-8f0b-2ce6a9bf7342",
        "outputId": "51c15b99-a392-4933-b512-03623df671ac"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "env: TAVILY_API_KEY=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\n"
          ]
        }
      ],
      "source": [
        "import os, getpass\n",
        "\n",
        "%env TAVILY_API_KEY = {userdata.get('TAVILY_API_KEY')}\n",
        "\n",
        "def _set_env(var: str):\n",
        "    if not os.environ.get(var):\n",
        "        os.environ[var] = getpass.getpass(f\"{var}: \")\n",
        "_set_env(\"TAVILY_API_KEY\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 38,
      "id": "bfb4f56c-3334-4927-8ed8-62fd384ee43e",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 350
        },
        "id": "bfb4f56c-3334-4927-8ed8-62fd384ee43e",
        "outputId": "57ad0e6a-b07f-4860-f269-fbb5e07fd4c3"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/jpeg": "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\n",
            "text/plain": [
              "<IPython.core.display.Image object>"
            ]
          },
          "metadata": {}
        }
      ],
      "source": [
        "from langchain_core.messages import HumanMessage, SystemMessage\n",
        "\n",
        "from langchain_community.document_loaders import WikipediaLoader\n",
        "from langchain_community.tools.tavily_search import TavilySearchResults\n",
        "\n",
        "def search_web(state):\n",
        "\n",
        "    \"\"\" Retrieve docs from web search \"\"\"\n",
        "\n",
        "    # Search\n",
        "    tavily_search = TavilySearchResults(max_results=3)\n",
        "    search_docs = tavily_search.invoke(state['question'])\n",
        "\n",
        "     # Format\n",
        "    formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n",
        "        [\n",
        "            f'<Document href=\"{doc[\"url\"]}\"/>\\n{doc[\"content\"]}\\n</Document>'\n",
        "            for doc in search_docs\n",
        "        ]\n",
        "    )\n",
        "\n",
        "    return {\"context\": [formatted_search_docs]}\n",
        "\n",
        "def search_wikipedia(state):\n",
        "\n",
        "    \"\"\" Retrieve docs from wikipedia \"\"\"\n",
        "\n",
        "    # Search\n",
        "    search_docs = WikipediaLoader(query=state['question'],\n",
        "                                  load_max_docs=2).load()\n",
        "\n",
        "     # Format\n",
        "    formatted_search_docs = \"\\n\\n---\\n\\n\".join(\n",
        "        [\n",
        "            f'<Document source=\"{doc.metadata[\"source\"]}\" page=\"{doc.metadata.get(\"page\", \"\")}\"/>\\n{doc.page_content}\\n</Document>'\n",
        "            for doc in search_docs\n",
        "        ]\n",
        "    )\n",
        "\n",
        "    return {\"context\": [formatted_search_docs]}\n",
        "\n",
        "def generate_answer(state):\n",
        "\n",
        "    \"\"\" Node to answer a question \"\"\"\n",
        "\n",
        "    # Get state\n",
        "    context = state[\"context\"]\n",
        "    question = state[\"question\"]\n",
        "\n",
        "    # Template\n",
        "    answer_template = \"\"\"Answer the question {question} using this context: {context}\"\"\"\n",
        "    answer_instructions = answer_template.format(question=question,\n",
        "                                                       context=context)\n",
        "\n",
        "    # Answer\n",
        "    answer = llm.invoke([SystemMessage(content=answer_instructions)]+[HumanMessage(content=f\"Answer the question.\")])\n",
        "\n",
        "    # Append it to state\n",
        "    return {\"answer\": answer}\n",
        "\n",
        "# Add nodes\n",
        "builder: StateGraph = StateGraph(State)\n",
        "\n",
        "# Initialize each node with node_secret\n",
        "builder.add_node(\"search_web\",search_web)\n",
        "builder.add_node(\"search_wikipedia\", search_wikipedia)\n",
        "builder.add_node(\"generate_answer\", generate_answer)\n",
        "\n",
        "# Flow\n",
        "builder.add_edge(START, \"search_wikipedia\")\n",
        "builder.add_edge(START, \"search_web\")\n",
        "builder.add_edge(\"search_wikipedia\", \"generate_answer\")\n",
        "builder.add_edge(\"search_web\", \"generate_answer\")\n",
        "builder.add_edge(\"generate_answer\", END)\n",
        "graph: CompiledStateGraph = builder.compile()\n",
        "\n",
        "display(Image(graph.get_graph().draw_mermaid_png()))"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 39,
      "id": "fa544ca0-10af-491e-ad7a-477d004413eb",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 107
        },
        "id": "fa544ca0-10af-491e-ad7a-477d004413eb",
        "outputId": "13748ef1-6ff0-4766-f8e0-14c34578d899"
      },
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'The provided text states that Nvidia made a **$6 billion profit** on **$13.5 billion in revenue** during Q2 2024. This information is mentioned in the first document, specifically in the first two sentences:\\n\\n> \"Nvidia just made $6 billion in pure profit over the AI boom\"\\n> \"On a staggering $13.5 billion in revenue.\" \\n\\nThe second document confirms these figures, providing the exact figures:\\n\\n> Earnings: $2.70 per share, adjusted, versus $2.09 per share expected by Refinitiv. Revenue: $13.51 billion versus $11.22 billion expected by Refinitiv. \\n\\nTherefore, Nvidia\\'s Q2 2024 earnings were **$6 billion in profit** on **$13.5 billion in revenue**. \\n'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 39
        }
      ],
      "source": [
        "result = graph.invoke({\"question\": \"How were Nvidia's Q2 2024 earnings\"})\n",
        "result['answer'].content"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "3dbbecab-80eb-4f0c-b43a-45542fc0ae9c",
      "metadata": {
        "id": "3dbbecab-80eb-4f0c-b43a-45542fc0ae9c"
      },
      "source": [
        "## Using with LangGraph API\n",
        "\n",
        "--\n",
        "\n",
        "**⚠️ DISCLAIMER**\n",
        "\n",
        "*Running Studio currently requires a Mac. If you are not using a Mac, then skip this step.*\n",
        "\n",
        "*Also, if you are running this notebook in CoLab, then skip this step.*\n",
        "\n",
        "--\n",
        "\n",
        "Let's load our the above graph in the Studio UI, which uses `module-4/studio/parallelization.py` set in `module-4/studio/langgraph.json`.\n",
        "\n",
        "![Screenshot 2024-08-29 at 3.05.13 PM.png](https://cdn.prod.website-files.com/65b8cd72835ceeacd4449a53/66dbb10f43c3d4df239e0278_parallelization-1.png)\n",
        "\n",
        "Let's get the URL for the local deployment from Studio."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "4bc8ad8d-1365-4801-a8a5-b85cd4965119",
      "metadata": {
        "id": "4bc8ad8d-1365-4801-a8a5-b85cd4965119"
      },
      "outputs": [],
      "source": [
        "import platform\n",
        "\n",
        "if 'google.colab' in str(get_ipython()) or platform.system() != 'Darwin':\n",
        "    raise Exception(\"Unfortunately LangGraph Studio is currently not supported on Google Colab or requires a Mac\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "23919dc9-27d8-4d10-b91d-24acdf8c0fb9",
      "metadata": {
        "id": "23919dc9-27d8-4d10-b91d-24acdf8c0fb9"
      },
      "outputs": [],
      "source": [
        "from langgraph_sdk import get_client\n",
        "client = get_client(url=\"http://localhost:63082\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "ff35e68f-4017-4f45-93cf-ddbb355a0bc1",
      "metadata": {
        "id": "ff35e68f-4017-4f45-93cf-ddbb355a0bc1",
        "outputId": "b2de58ae-dcca-470c-a103-1b148f99a66c"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Nvidia's Q2 2024 earnings were exceptionally strong. The company reported $13.5 billion in revenue, significantly surpassing expectations, and made $6 billion in pure profit. The earnings per share were $2.70, adjusted, compared to the $2.09 per share expected by analysts. The gross profit margins were 75.1%, and the adjusted earnings per share were 68 cents. The strong performance was driven by unprecedented demand for its generative AI chips.\n"
          ]
        }
      ],
      "source": [
        "thread = await client.threads.create()\n",
        "input_question = {\"question\": \"How were Nvidia Q2 2024 earnings?\"}\n",
        "async for event in client.runs.stream(thread[\"thread_id\"],\n",
        "                                      assistant_id=\"parallelization\",\n",
        "                                      input=input_question,\n",
        "                                      stream_mode=\"values\"):\n",
        "    # Check if answer has been added to state\n",
        "    answer = event.data.get('answer', None)\n",
        "    if answer:\n",
        "        print(answer['content'])"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3 (ipykernel)",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.4"
    },
    "colab": {
      "provenance": []
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}